package com.mikufufu.blog.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * {@code @Aspect} 注解用于标识一个类为切面类，切面类中可以包含多个通知方法。
 * 切面类可以包含多个通知方法，每个通知方法都可以用于拦截特定的方法调用，并进行相应的处理。
 * 切面类还可以包含其他的切面类，以实现更复杂的切面逻辑。
 */
@Slf4j
@Aspect
@Component
public class MessageAspect {

    /**
     * 切入点，用于标识需要拦截的方法
     * {@code @Pointcut("execution(* com.mikufufu.blog.listener.BaseListener.onMessage(..))")}
     * {@code execution(* com.mikufufu.blog.listener.BaseListener.onMessage(..))}表示拦截 com.example.demo.listener 包下所有以 BaseListener 开头且参数类型为任意的函数
     */
    @Pointcut("execution(* com.mikufufu.blog.listener.BaseListener.onMessage(..))")
    public void listenerMessage() {}

    /**
     * 前置通知，在目标方法执行前执行
     * @param joinPoint 切入点
     */
    @Before("listenerMessage()")
    public void beforeListenerMessage(JoinPoint joinPoint) {
//        Object[] args = joinPoint.getArgs();
        log.debug("方法: {}", joinPoint.getSignature().getName());
        log.debug("类: {}", joinPoint.getSignature().getDeclaringTypeName());
//        log.debug("参数: {}", Arrays.toString(args));
    }

    @After("listenerMessage()")
    public void afterListenerMessage(JoinPoint joinPoint) {
        log.debug("方法执行结束: {}", joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知，可以在目标方法执行前后执行自定义逻辑，并返回目标方法的执行结果
     * @param joinPoint 切入点
     * @return 目标方法的执行结果
     * @throws Throwable 如果目标方法抛出异常，环绕通知也会捕获并抛出该异常
     */
    @Around("listenerMessage()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 执行目标方法
        long duration = System.currentTimeMillis() - startTime;
        log.debug("[AOP] 方法 {} 执行耗时 {} ms", joinPoint.getSignature().getName(), duration);
        return result;
    }

}
